Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Infer from generic function return types #16072

Merged
merged 12 commits into from
May 26, 2017
Merged

Conversation

ahejlsberg
Copy link
Member

@ahejlsberg ahejlsberg commented May 25, 2017

With this PR we improve type argument inference by inferring from the contextual type of a generic function call to the return type of the generic function. For example:

function emptyArray<T>(): T[] {
    return [];
}

let a1: string[] = emptyArray();  // string inferred for T
let a2: number[] = emptyArray();  // number inferred for T

Previously the two assignments above would error because {} was inferred for T. We now infer from the contextual type (i.e. the type of the variable to which the function result is assigned).

A more elaborate example:

function arrayFilter<T>(f: (x: T) => boolean): (a: T[]) => T[] {
    return a => a.filter(f);
}

function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[] {
    return a => a.map(f);
}

function compose<A, B, C>(f: (x: A) => B, g: (x: B) => C): (x: A) => C {
    return x => g(f(x));
}

const positives: (a: number[]) => number[] = arrayFilter(x => x >= 0);
const lengths: (a: string[]) => number[] = arrayMap(s => s.length);
const evenLengths: (a: string[]) => number[] =
    compose(arrayMap(s => s.length), arrayFilter(x => x % 2 === 0));

let a1 = positives([1, 2, -3, 4, -5]);            // [1, 2, 4]
let a2 = lengths(['a', 'bb', 'ccc', 'dddd']);     // [1, 2, 3]
let a3 = evenLengths(['a', 'bb', 'ccc', 'dddd']); // [2, 4]

Inferences made from generic function return types have lower priority than inferences made from arguments to the generic function. For example, the type of s is inferred as string (not Object) in the following:

function filter<T>(a: T[], f: (x: T) => boolean): T[] {
    return a.filter(f);
}

let a: Object[] = filter(['a', 'bb', 'ccc', 'dddd'], s => s.length > 2);

This PR is a precursor for inferring higher order function types when no inferences can be made for one or more type parameters.

const wrapper = compose(x => [x], y => ({ p: y }));  // (x: {}) => { p: {}[] }

We currently infer (x: {}) => { p: {}[] }, but ideally we'd infer <A>(x: A) => { p: A[] }. That's next on the list.

Fixes #15680.

@benlesh
Copy link

benlesh commented May 25, 2017

I was told "if you get Anders interested in your typings issue, it'll be implemented in a few hours"

Apparently the legend is real.

@@ -10288,27 +10293,24 @@ namespace ts {
// it as an inference candidate. Hopefully, a better candidate will come along that does
// not contain anyFunctionType when we come back to this argument for its second round
// of inference.
if (source.flags & TypeFlags.ContainsAnyFunctionType) {
if (source.flags & TypeFlags.ContainsAnyFunctionType || source === silentNeverType) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

worth adding a note on the use of silentNeverType as a marker here.

inferences.primary || (inferences.primary = []);
if (!contains(candidates, source)) {
candidates.push(source);
for (const inference of inferences) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getInferenceInfoForType here as well?

@RyanCavanaugh
Copy link
Member

Why doesn't this fix #11152 ?

@mhegazy
Copy link
Contributor

mhegazy commented May 30, 2017

Why doesn't this fix #11152 ?

contextual type is not propagated/instantiated correctly yet.

@KiaraGrouwstra
Copy link
Contributor

@ahejlsberg:

We currently infer (x: {}) => { p: {}[] }, but ideally we'd infer <A>(x: A) => { p: A[] }. That's next on the list.

If that's still outstanding, might there be any PR/branch we could follow on that?

@johnnyreilly
Copy link

contextual type is not propagated/instantiated correctly yet.

@mhegazy - I'm liking the use of the word "yet" above 👍 😄

@AlexGalays
Copy link

Where can we track progress against the following bug?

type Function1<T1, R> = (t1: T1) => R


function pipe<T1, R>(fn: Function1<T1, R>): Function1<T1, R> {
    return fn
}


/// test

function identity<A>(a: A): A { return a }


const pipedIdentity = pipe(identity)

// x is a {}, not a number
const x = pipedIdentity(33)

@gcnew
Copy link
Contributor

gcnew commented Nov 2, 2017

@AlexGalays see #9366 and the linked issues.

@mhegazy mhegazy deleted the improveTypeArgumentInference branch November 2, 2017 19:09
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants